//Almost5 schedule program

//This is the main part of the program. If you want to run the whole thing you will need to run this file
import java.util.*;
import java.io.*;
import java.lang.reflect.Array;
public class schedule_main
{
	//This is the main data structure
   //It uses the department code as the key eg. BIO and an arrayList of all the classes that belong to that department
	static HashMap<String,ArrayList<course>> dataStruct = new HashMap<String,ArrayList<course>>();
   
	public static void main (String[] args) throws IOException
	{
     //read in the correctly formatted list of courses
     //it hasn't been created yet
		readFile();
		
		
		Scanner in = new Scanner(System.in);
		String choice = "";
		while(!choice.equalsIgnoreCase("0"))
		{
			System.out.println();
			System.out.println("WELCOME TO THE PROTOTYPE OF THE ALMOST5 SCHEDULE PROGRAM");
			System.out.println();
			System.out.println("Please choose an option: ");
			System.out.println("1) List the departments");
			System.out.println("2) See all classes");
			System.out.println("3) Test Algorithm");
			System.out.println("0) Quit");
			System.out.print("Your choice: ");
			choice = in.nextLine();
			System.out.println();
			if(choice.equalsIgnoreCase("1"))
				printDepartments(dataStruct.keySet());
			else if(choice.equalsIgnoreCase("2"))
				printAllCourses();
			else if(choice.equalsIgnoreCase("3"))
				runAlgoMenu();
			else if(!choice.equalsIgnoreCase("0"))
				System.out.println("Invalid Option. Please choose again.");
			
		}
		//hit zero to get here. 

		
		
	}
	public static void runAlgoMenu()
	{
		Scanner in = new Scanner(System.in);
		String choice = "";
		ArrayList<course> courses = new ArrayList<course>();
		ArrayList<course> coursesToRun = new ArrayList<course>();
		System.out.println();
		System.out.println("THIS IS THE TESTING SECTION FOR OUR ALGORITHM");
		System.out.println();
		System.out.println("ADD A CLASS");
		do{
			System.out.print("Department: ");
			String department = in.nextLine().toUpperCase().trim();
			if(dataStruct.containsKey(department))
			{
				courses = dataStruct.get(department);
				System.out.print("Course Number, ex. 307: ");
				String courseNum = in.nextLine().toUpperCase().trim();
				String testString = department+ " " +courseNum;
				boolean isValid = false;
				for(int x=0;x<courses.size();x++)
				{
					course temp = courses.get(x);
//					System.out.println(temp.getcourseID().trim().equalsIgnoreCase(testString));
//					System.out.println(temp.getcourseID().trim()+ " "+testString);
					if(temp.getcourseID().trim().equalsIgnoreCase(testString))
					{
						coursesToRun.add(temp);
						isValid=true;
					}	
				}
				if(!isValid)
					System.out.println("That is an invalid class. Please try again.");
				
			}
			else
			{
				System.out.println("Invalid department code. Please try again.");
			}
			
			
			System.out.print("Add another class(y/n): ");
			choice = in.nextLine();
		} while(choice.equalsIgnoreCase("Y"));
		
		//We will now ask if they have any predetermined spaces that they want to save
		System.out.print("Do you have any blocks of time that you would like to reserve? (y/n): ");
		choice = in.nextLine();
		if(choice.equalsIgnoreCase("y"))
		{
			createTimeBlock(coursesToRun);
			do{
				System.out.print("Do you have any more blocks to reserve? (y/n): ");
				choice = in.nextLine();
				if(choice.equalsIgnoreCase("y"))
					createTimeBlock(coursesToRun);
			}while(!choice.equalsIgnoreCase("n"));
		}
			
		if(coursesToRun.size()>0)
		{
			ArrayList<Schedule> allSchedules=generateSchedules(coursesToRun);
			runPrioritiesMenu(allSchedules);
			System.out.println("There were "+allSchedules.size()+" schedules possible");
			
			for(int x=0;x<allSchedules.size();x++)
			{
				Schedule s = allSchedules.get(x);
				//System.out.println(s.hasProf("ebr"));
				s.print();
			}
		}		
	}
	
	public static void createTimeBlock(ArrayList<course> t)
	{
		Scanner in = new Scanner(System.in);
		System.out.print("	Please put the shortened version of the day ie MTWTHF. Day: ");
		String day = in.nextLine().toUpperCase();
		System.out.print("	Please put the time in miltary time with a dash and no spaces. Time: ");
		String time = in.nextLine();
		course newCourse = new course();
		Unique u = new Unique(day,time);
		newCourse.addUnique(u);
		t.add(newCourse);
	}
	/*
	 *This method asks all the priority questions 
	 *
	 *If you have another way to word these questions please let me know.
	 *I put them in this method alone because that way when we move it to a site
	 *the code will all be able to be run.
	 *
	 */
	public static void runPrioritiesMenu(ArrayList schedules)
	{
		Scanner in = new Scanner(System.in);
		ArrayList<String> choices = new ArrayList<String>();
		String choice = "";
		System.out.println();
		System.out.println("PRIORITIES MENU");
		System.out.println("*** please enter your preferences from least to greatest. You can use all of them or none of them. Just press 0 to get your results ***");
		System.out.println("1) Earliest Starting time");
		System.out.println("2) Latest Starting time");
		System.out.println("3) Earliest Ending time");
		System.out.println("4) Professor preference");
		System.out.println("5) Lunch Break");
		System.out.println("6) Most Classes MWF");
		System.out.println("7) Most Classes TTH");
		System.out.println("0) Done");
		do
		{
			System.out.print("Choice: ");
			choice = in.nextLine();
			choices.add(choice);
		} while(!choice.equalsIgnoreCase("0"));
		
		for(int x=0;x<choices.size();x++)
		{
			if(choices.get(x).equalsIgnoreCase("1"))
			{
				startingEarliest(schedules);
			}
			else if(choices.get(x).equalsIgnoreCase("2"))
			{
				startingLatest(schedules);
			}
			else if(choices.get(x).equalsIgnoreCase("3"))
			{
				endingEarliest(schedules);
			}
			else if(choices.get(x).equalsIgnoreCase("4"))
			{
				System.out.print("	Which professor? Please enter in their last name. Professor: ");
				choice = in.nextLine();
				professorSort(schedules,choice);
			}
			else if(choices.get(x).equalsIgnoreCase("5"))
			{
				lunchBreak(schedules);
			}
			else if(choices.get(x).equalsIgnoreCase("6"))
			{
				mostMWFClasses(schedules);
			}
			else if(choices.get(x).equalsIgnoreCase("7"))
			{
				mostTTHClasses(schedules);
			}
		}
		
	} 
		
	//Prints all the classes underneath their respective department headings
	public static void printAllCourses()
	{
		for(Iterator it = dataStruct.keySet().iterator();it.hasNext();)
		{
			String dept = (String)it.next();
			System.out.printf("\t%s\n",dept);
			ArrayList<course> classes= dataStruct.get(dept);
			for(course c : classes)
				System.out.printf("\t  %s\n",c);	
		}
	}
	//Print all the departments
	public static void printDepartments(Set t)
	{
		System.out.println("THE DEPARTMENTS ARE: ");
		for(Iterator it = t.iterator();it.hasNext();)
		{
			System.out.printf("\t%s\n",it.next());
		}
		
	}
	//this will be the method to generate the schedules. or at least my take on it
	public static ArrayList<Schedule> generateSchedules(ArrayList<course> classes)
	{
		ArrayList<Schedule> allPossibleSchedules = new ArrayList<Schedule>();
		allPossibleSchedules = generatorHelper(classes,0,new ArrayList<Unique>(),new ArrayList<Schedule>());
		return allPossibleSchedules;
	}
	private static ArrayList<Schedule> generatorHelper(ArrayList<course> classes, int index, ArrayList<Unique> classesSoFar, ArrayList<Schedule> createdSchedules)
	{
		
		//base case
		if(classesSoFar.size()==classes.size()) // you have the same number of uniques as classes so stop 
		{
			//System.out.println(createdSchedules);
			//System.out.println(classes);
			//System.out.println(classesSoFar);
			Schedule newSchedule = new Schedule(classes,classesSoFar);
			createdSchedules.add(newSchedule);
			classesSoFar=new ArrayList<Unique>();
			return createdSchedules;
		}
		else
		{
			//System.out.println(classes.get(index)+ " " + index);
			ArrayList<Unique> classUniques = classes.get(index).getUniques(); //get all the uniques of this specific class
			//System.out.println(classUniques);
			for(int x=0;x<classUniques.size();x++)
			{
				Unique temp = classUniques.get(x); //one of the uniqiues of this course
				if(checkValidity(classesSoFar,temp)) //if it works with the prev uniques
				{
					classesSoFar.add(temp); //add it to the classes you have been working with so far
					generatorHelper(classes,index+1,classesSoFar,createdSchedules);
					classesSoFar.remove(temp);
				}
			}
			
		}
		return createdSchedules;
	}
	
	// a method that checks to see if a given unique clashes with any other unique
	// return true if its a valid unique
	// returns false if it clashes with any other unique
	private static boolean checkValidity(ArrayList<Unique> classesSoFar, Unique newClass)
	{
		for(int x=0;x<classesSoFar.size();x++)
		{
			Unique temp = classesSoFar.get(x);
			if(!temp.compare(newClass))
				return false;
		}
		return true;
	}
	
	// Sorts an arrayList in order starting with latest start time
	public static void startingLatest(ArrayList<Schedule> schedules){
		Schedule [] array = new Schedule [schedules.size()];
		for(int i = 0; i < array.length; i++){
			array[i] = schedules.get(i);
		}
		Arrays.sort(array, new latestStartComparator());
		for(int i = 0; i < array.length; i++){
			schedules.set(i, array[i]);
		}
	}
	
	// Sorts an arrayList in order starting with earliest start time
	public static void startingEarliest(ArrayList<Schedule> schedules){		
		
		Schedule [] array = new Schedule [schedules.size()];
		for(int i = 0; i < array.length; i++){
			array[i] = schedules.get(i);
		}
		Arrays.sort(array, new earliestStartComparator());
		for(int i = 0; i < array.length; i++){
			schedules.set(i, array[i]);
		}
	}

	public static void endingEarliest(ArrayList<Schedule> schedules){	
		
		Schedule [] array = new Schedule [schedules.size()];
		for(int i = 0; i < array.length; i++){
			array[i] = schedules.get(i);
		}
		Arrays.sort(array, new earliestEndingComparator());
		for(int i = 0; i < array.length; i++){
			schedules.set(i, array[i]);
		}
	}
	
	public static void mostMWFClasses(ArrayList<Schedule> schedules){	
		
		Schedule [] array = new Schedule [schedules.size()];
		for(int i = 0; i < array.length; i++){
			array[i] = schedules.get(i);
		}
		Arrays.sort(array, new mostMWF());
		for(int i = 0; i < array.length; i++){
			schedules.set(i, array[i]);
		}
	}
	
public static void mostTTHClasses(ArrayList<Schedule> schedules){	
		
		Schedule [] array = new Schedule [schedules.size()];
		for(int i = 0; i < array.length; i++){
			array[i] = schedules.get(i);
		}
		Arrays.sort(array, new mostTTH());
		for(int i = 0; i < array.length; i++){
			schedules.set(i, array[i]);
		}
	}
	
	public static void lunchBreak(ArrayList<Schedule> schedules)
	{
		Schedule [] array = new Schedule [schedules.size()];
		int counter = 0;
		for(int i = 0; i < array.length; i++){
			if(!schedules.get(i).hasLunchBreak())
				counter++;
			array[i] = schedules.get(i);
		}
		//System.out.println(counter+" " +schedules.size());
		if(counter==schedules.size())
			System.out.println("There were not any schedules with a lunch break. Sorry!");
		else
		{
			Arrays.sort(array, new lunchComparator());
			for(int i = 0; i < array.length; i++){
				schedules.set(i, array[i]);
			}
		}
		
	}
	
	public static void professorSort(ArrayList<Schedule> schedules, String prof)
	{
		ArrayList<Schedule> temp = new ArrayList<Schedule>();
		for(int x=0;x<schedules.size();x++)
		{
			Schedule s = schedules.get(x);
			if(s.hasProf(prof))
				temp.add(0,s);
			else
				temp.add(s);
		}
		schedules=temp;
	}
   
   /*
     This method does all the reading of the text file with all the real information.
     If you want to understand all the individual pieces below go read the test.txt. It
     has been correctly formatted and will allow you to see how this program works.
     
     One of the unfortunate side effects of the way I did this is that the 
     input file has to be formatted exactly the right way. If it doesn't
     you are going to run into problems
     */
	public static void readFile()throws IOException
	{
		Scanner inFile = new Scanner(new File("test.txt"));
		int counter = 1; //This will keep track of what line we are on
		String course_id = "";
		String course_name = "";
		String course_days = "";
		String course_time = "";
		String course_location = "";
		String course_Instructor = "";
		ArrayList<Unique> allUniques = new ArrayList<Unique>();
		Integer unique = 0;
		
		while(inFile.hasNext())
		{
			String line = inFile.nextLine();
			if(line.length()==0||line.charAt(0)=='*') //Empty line between all the new classes
			{
				counter = 0;
				course newCourse = new course(course_id,course_name,allUniques); //creates a new course with all of its unique classes
				allUniques = new ArrayList<Unique>();
				String[] temp = course_id.split(" ");
				String key=temp[0];
//				for(int x=0;x<temp.length-1;x++)
//					key +=temp[x];
				if(!dataStruct.containsKey(key)) //if this course is the first one in the department
				{
					ArrayList<course> deptCourses = new ArrayList<course>();
					deptCourses.add(newCourse); //add the new course
					dataStruct.put(key,deptCourses); //restore the arraylist to the datastructure
				}
				else
				{
					ArrayList<course> deptCourses = dataStruct.get(key); //get the arraylist of courses this department has
					deptCourses.add(newCourse); //add the new course
					dataStruct.put(key,deptCourses); //restore the arraylist to the datastructure
				}			
				
			}
			else if(counter==1) //This is the class and id. Ex. C S 307
			{
				String[] dept = line.split(" ");
				//System.out.println(dept.length);
				if(dept.length<=2)
					course_id=line;
				else
				{
					String temp = dept[0]+dept[1]+" "+dept[2];
					course_id=temp;
				}
			}
			else if(counter==2) //This is the class title. Ex. Foundations of Computer Science
			{
				course_name=line;
			}
			else if(counter>2) //This is all the uniques and class info.
			{
				String[] info = line.split(" ");
				if(info[0].length()==5) //It is the unique number
				{
					 unique = Integer.parseInt(info[0]);
					 course_days=info[1]; //store the days that the class is held normally
					 if(info[4].charAt(info[4].length()-1)=='P') //if the class is in the afternoon, add 1200 to make it military time
					 {
					 	String beginTime = info[2];
					 	String endTime = info[4].substring(0,info[4].length()-1);
					 	Integer bT = Integer.parseInt(beginTime);
					 	Integer eT = Integer.parseInt(endTime);
					 	if(beginTime.length()==3) // checks to make sure that the class isn't in the morning
					 		bT+=1200;
					 	if(endTime.length()==3)  //checks to make sure that the class isn't in the morning
					 		eT+=1200;
					 	course_time=bT+"-"+eT;
					 }
					 else //create the time String for the morning classes
					 {
					 	String beginTime = info[2];
					 	String endTime = info[4];
					 	if(beginTime.length()==3)
					 		beginTime = "0"+beginTime;
					 	if(endTime.length()==3)
					 		endTime="0"+endTime;
					 	course_time=beginTime+"-"+endTime;
					 }
					 course_location=info[5]+" "+info[6];
					 if(info.length>7)
					 {
					 	course_Instructor=info[7]+" "+info[8];
					 }
					 Unique temp = new Unique(unique,course_Instructor,course_location,course_days,course_time); //create a new unique class
					 allUniques.add(temp); //add it to the list of this courses unique
				}
				else //this is the second time that a class meets if it does
				{
					course_days=info[0];
					if(info[3].charAt(info[3].length()-1)=='P') //if the class is in the afternoon, add 1200 to make it military time
					{
					 	String beginTime = info[1];
					 	String endTime = info[3].substring(0,info[3].length()-1);
					 	Integer bT = Integer.parseInt(beginTime);
					 	Integer eT = Integer.parseInt(endTime);
					 	if(beginTime.length()==3) // checks to make sure that the class isn't in the morning
					 		bT+=1200;
					 	if(endTime.length()==3)  //checks to make sure that the class isn't in the morning
					 		eT+=1200;
					 	course_time=bT+"-"+eT;
					 }
					 else //create the time String for the morning classes
					 {
					 	String beginTime = info[1];
					 	String endTime = info[3];
					 	if(beginTime.length()==3)
					 		beginTime = "0"+beginTime;
					 	if(endTime.length()==3)
					 		endTime="0"+endTime;
					 	course_time=beginTime+"-"+endTime;
					 }
					 course_location=info[4]+" "+info[5];
					 Unique prevUnique = allUniques.get(allUniques.size()-1); //gets the previous unique class
					 prevUnique.addDay(course_location,course_days,course_time); //adds the extra info to that unique
				}
					
			}
			counter++;
		}		
		
	}
}
class earliestStartComparator implements Comparator{
	public int compare(Object emp1, Object emp2){
		return ((Schedule)emp1).getStartTime().compareTo(((Schedule)emp2).getStartTime());
	}
}
class latestStartComparator implements Comparator{
	public int compare(Object emp1, Object emp2){
		return ((Schedule)emp2).getStartTime().compareTo(((Schedule)emp1).getStartTime());		
	}
}
class lunchComparator implements Comparator{
	public int compare(Object emp1, Object emp2)
	{
		if(((Schedule)emp1).hasLunchBreak())
			if(((Schedule)emp2).hasLunchBreak())
				return 0;
			else
				return 1;
		else if(((Schedule)emp2).hasLunchBreak())
			return -1;
		else
			return 0;
	}

}


class earliestEndingComparator implements Comparator{
	public int compare(Object emp1, Object emp2){
		return ((Schedule)emp1).getEndTime().compareTo(((Schedule)emp2).getEndTime());		
	}
}

class mostMWF implements Comparator{
	public int compare(Object emp1, Object emp2)
	{
		if(((Schedule)emp1).alldays[0] > ((Schedule)emp2).alldays[0])
			return -1;
		else if(((Schedule)emp1).alldays[0] == ((Schedule)emp2).alldays[0])
			return 0;
		else
			return 1;
	}
}

class mostTTH implements Comparator{
	public int compare(Object emp1, Object emp2)
	{
		if(((Schedule)emp1).alldays[1] > ((Schedule)emp2).alldays[1])
			return -1;
		else if(((Schedule)emp1).alldays[1] == ((Schedule)emp2).alldays[1])
			return 0;
		else
			return 1;
	}
}